package com.ant.backstage.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ant.backstage.dao.PermissionMapper;
import com.ant.backstage.dao.UserInfoMapper;
import com.ant.backstage.dao.UserMapper;
import com.ant.backstage.dao.UserroleMapper;
import com.ant.backstage.facade.ApiReturn;
import com.ant.backstage.facade.code.ApiReturnCode;
import com.ant.backstage.facade.dto.OldPwdMapDTO;
import com.ant.backstage.facade.dto.UserDTO;
import com.ant.backstage.facade.enums.UserStatusEnum;
import com.ant.backstage.facade.enums.VerificationTypeEnum;
import com.ant.backstage.facade.model.User;
import com.ant.backstage.facade.model.UserInfo;
import com.ant.backstage.facade.model.Userrole;
import com.ant.backstage.facade.request.registerLogin.FindPwdRequst;
import com.ant.backstage.facade.request.registerLogin.UserRegisterRequest;
import com.ant.backstage.service.RegisterAndLoginService;
import com.ant.backstage.utils.RandomUtils;
import com.ant.backstage.utils.RedisUtil;
import com.ant.backstage.utils.jwt.JwtUtil;
import com.ant.backstage.utils.jwt.SaltUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 * @date 2021/12/23 12:33
 **/
@Service("registerAndLoginService")
public class RegisterAndLoginServiceImpl implements RegisterAndLoginService {

    @Resource
    UserMapper userMapper;
    @Resource
    PermissionMapper permissionMapper;
    @Resource
    UserInfoMapper userInfoMapper;
    @Resource
    UserroleMapper userroleMapper;
    @Resource
    RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiReturn register(UserRegisterRequest request){
        ApiReturn apiReturn = new ApiReturn();
        if (!request.getNewPwd().equals(request.getConfirmPwd()) ||
                StringUtils.isBlank(request.getUname()) ||  StringUtils.isBlank(request.getGender()) ||
                StringUtils.isBlank(request.getMotto()) ||  StringUtils.isBlank(request.getNewPwd())
        ) {
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
            apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
            return apiReturn;
        }

        User user = new User();
        UserInfo userInfo = new UserInfo();
        String uuid = IdUtil.simpleUUID();
        //1.获取随机盐
        String salt = SaltUtil.getSalt(8);
        //2.将随机盐保存到数据
        user.setSalt(salt);
        //3.明文密码进行md5 + salt + hash散列
        String newPwd = request.getNewPwd();
        Md5Hash MD5 = new Md5Hash(newPwd,salt,1024);
        user.setPwd(MD5.toHex());
        user.setUid(uuid); //糊涂工具库IdUtil
        user.setUname(request.getUname());
        user.setCreatedatetime(new Date());
        user.setAge(Integer.valueOf(request.getAge()));
        user.setStatus(UserStatusEnum.USER_STATE_NORMAL.getCode());
        user.setGender(request.getGender());
        user.setUphone(request.getUphone());
        user.setLastupdatetime(new Date());
        user.setAddressid(1);//户籍地的编码 暂留未实现
        userInfo.setUid(uuid);
        userInfo.setMail(request.getMail());
        userInfo.setCompany(request.getCompany());
        userInfo.setCreatedatetime(new Date());
        userInfo.setLastupdatetime(new Date());
        userInfo.setAvatarImgUrl(request.getAvattarImg());
        userInfo.setCardurl(request.getIdCardNoImg());
        userInfo.setIdcardno(request.getIdCardNo());
        userInfo.setMotto(request.getMotto());
        userInfo.setPosition(request.getPosition());
        userInfo.setPlaceNow(request.getPlaceNow());
        userMapper.insert(user);
        userInfoMapper.insert(userInfo);
        //赋予用户基本角色
        Userrole userrole = new Userrole();
        userrole.setUserUid(uuid);
        userrole.setRoleId(7);
        userroleMapper.insert(userrole);
        apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
        return apiReturn;
    }

    @Override
    public User findByUsername(String uname) {
        return userMapper.selectbyName(uname);
    }

    @Override
    public String  generateJwt(Map<String, String> claims) {
        // 8个字符的随机字符串，作为生成jwt的随机盐
        // 保证每次登录成功返回的Token都不一样
        String jwtSecret = RandomUtils.generateStr(8);
        // 将此次登录成功的jwt secret存到数据库，下次携带jwt时解密需要使用
        userMapper.updateJwtSecretByUid(claims.get("uid"), jwtSecret);
        //刷新token时会重新生成token 和加密盐，但是并没有再查数据库，所有需要在此添加
        String token = JwtUtil.generateJwt(claims, jwtSecret, TimeUnit.SECONDS.toMillis(60*10));// 有效期 10分钟
        Subject subject = SecurityUtils.getSubject();
        UserDTO dto = (UserDTO) subject.getPrincipal();
        if (Optional.ofNullable(dto).isPresent()) {
            //切换身份，登录后，动态更改subject的用户属性 新token，和盐
            dto.setToken(token);
            dto.setJwtSecret(jwtSecret);
            PrincipalCollection principalCollection = subject.getPrincipals();
            String realmName = principalCollection.getRealmNames().iterator().next();
            PrincipalCollection newPrincipalCollection =
                    new SimplePrincipalCollection(dto, realmName);
            subject.runAs(newPrincipalCollection);
        }
        return token;
    }

    @Override
    public void logout(String uid) {
        // 将jwt secret置为空
        userMapper.updateJwtSecretByUid(uid, null);
    }

    @Override
    public User findByUid(String uid) {
        return userMapper.selectByUid(uid);
    }

    @Override
    public User login(String uname, String pwd) {
        User user = userMapper.selectbyName(uname);
        if (user == null) {
            return null; // 用户名不存在
        }
        String salt = user.getSalt();
        // 以注册时的相同规则，加密用户输入的密码
        Md5Hash md5Hash = new Md5Hash(pwd, salt, 1024);
        String s = md5Hash.toHex();
        // 比对密码
        return user.getPwd().equals(md5Hash.toHex()) ? user : null;
    }

    @Override
    public List<String> findPermissionsByUid(String uid) {
        List<String> permissionsList = permissionMapper.findPermissionsByUid(uid);
        return permissionsList;
    }

    /**
     * @Author zhangyu
     * @Description  
     * @Date 9:53 2021/12/30
     * @Param 重置密码，校验手机号，以及用户名，重置密码
     * @return 后期优化点： 1. 添加身份证号校验，短信校验，重置密码成功提醒，2.添加重置日志密码记录
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiReturn findPwd(ApiReturn apiReturn, FindPwdRequst findPwdRequst) {
        User user = userMapper.selectbyName(findPwdRequst.getUname());
        if (user == null) {
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getMessage());
            return apiReturn;
        }
        if (!findPwdRequst.getNewPwd().equals(findPwdRequst.getConfirmPwd())) {
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
            apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
            return apiReturn;
        }
        //校验成功 ，加密新密码重置密码
        //1.获取随机盐
        String salt = SaltUtil.getSalt(8);
        //3.明文密码进行md5 + salt + hash散列
        Md5Hash MD5 = new Md5Hash(findPwdRequst.getNewPwd(),salt,1024);
        //将老密码 保存到userInfo表  老密码  被修改时间
        UserInfo userInfo = userInfoMapper.selectbyUid(user.getUid());
        if (Optional.ofNullable(userInfo).isPresent()) {
            String oldPwd = userInfo.getOldPwd();
            Map hashMap = JSON.parseObject(oldPwd, HashMap.class);
            OldPwdMapDTO oldPwdMapDTO = new OldPwdMapDTO();
            oldPwdMapDTO.setOldPwd(user.getPwd());
            oldPwdMapDTO.setUpdateTime(new Date().getTime());
            oldPwdMapDTO.setUpdateType(findPwdRequst.getCheckType());
            if (hashMap==null) {
                hashMap = new HashMap<String,Object>();
            }
            hashMap.put(String.valueOf(new Date().getTime()), oldPwdMapDTO);
            userInfo.setLastupdatetime(new Date());
            userInfo.setOldPwd(JSONObject.toJSONString(hashMap));
            userInfoMapper.updateByPrimaryKey(userInfo);
        }else {
            UserInfo uInfo = new UserInfo();
            Map<String, Object> map = new HashMap<>();
            OldPwdMapDTO oldPwdMapDTO = new OldPwdMapDTO();
            oldPwdMapDTO.setOldPwd(user.getPwd());
            oldPwdMapDTO.setUpdateTime(new Date().getTime());
            oldPwdMapDTO.setUpdateType(findPwdRequst.getCheckType());
            map.put(String.valueOf(new Date().getTime()), oldPwdMapDTO);

            uInfo.setOldPwd(JSONObject.toJSONString(map)).setLastupdatetime(new Date())
                    .setCreatedatetime(new Date()).setUid(user.getUid());
            if (findPwdRequst.getCheckType().equals(VerificationTypeEnum.VERIFICATION_MAIL.getCode())) {
                uInfo.setMail(findPwdRequst.getUserContact());
            }
            userInfoMapper.insert(uInfo);
        }
        user.setPwd(MD5.toHex());
        //将随机盐保存到数据 todo 更新新密码盐
        userMapper.updatePwdByUid(user.getUid(),user.getPwd(),salt);
        //清除改用户下的缓存
        redisUtil.delPrefixStr(user.getUid());
        redisUtil.delPrefixStr(user.getUname());
        //重置成功短信
        apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
        return apiReturn;
    }

}
